#include "fsprojectpersistenceassistant.h"

#include "../documents/swpprojectdocument.h"

#include <fspersistencecommon.h>
#include <fsprojectpersistentdocument.h>
#include <idocument.h>
#include <ilinkeddocument.h>
#include <istorage.h>
#include <persistence.h>

#include <QtCore/QDataStream>
#include <QtCore/QDir>
#include <QtCore/QList>
#include <QtCore/QSet>
#include <QtCore/QUrl>

using namespace core;
using namespace fspersistence;
using namespace swp;

typedef QSharedPointer<IDocument> Document;

FsProjectPersistenceAssistant::FsProjectPersistenceAssistant(
    const MimeType& mimeType) : IPersistenceAssistant(),
        m_mimeType(mimeType),
        m_persistentDocument(),
        m_document()
{

}

FsProjectPersistenceAssistant::~FsProjectPersistenceAssistant()
{

}

// IPersistenceAssistant
bool FsProjectPersistenceAssistant::canOpen(const MimeType& mimeType) const
{
    return mimeType == m_mimeType;
}

bool FsProjectPersistenceAssistant::canSave(const MimeType& mimeType) const
{
    return mimeType == m_mimeType;
}

bool FsProjectPersistenceAssistant::isNative(
    const QSharedPointer<IPersistentDocument>& document) const
{
    return document->mimeType() == m_mimeType;
}

bool FsProjectPersistenceAssistant::isNative(const MimeType& mimeType) const
{
    return mimeType == m_mimeType;
}

QString FsProjectPersistenceAssistant::forStorage() const
{
    return FILE_SYSTEM_STORAGE;
}

QSharedPointer<IDocument> FsProjectPersistenceAssistant::open()
{
    Q_ASSERT(!m_persistentDocument.isNull());

    QSharedPointer<IDocument> ret;

    if (m_document.isNull() || m_document->isFuture())
    {
        const QSharedPointer<FsProjectPersistentDocument> _document =
            qSharedPointerDynamicCast<FsProjectPersistentDocument>(
                m_persistentDocument);

        Q_ASSERT(!_document.isNull());

        const IStorage* storage = _document->storage();
        const QByteArray data = storage->read(m_persistentDocument);
        _document->updateData(data);

        QDataStream stream(data);
        stream.setVersion(QDataStream::Qt_4_7);

        QString name;
        QList<QString> categories;
        QHash<QString, QString> links;
        QHash<QString, QSharedPointer<ILinkedDocument> > linksMapping;

        stream >> name;
        stream >> categories;
        stream >> links;

        QSharedPointer<IContainerDocument> projDocument(
            new SwpProjectDocument(name, _document->mimeType()));

        const QString projectUrl =
            m_persistentDocument->url().toLocalFile();
        QDir projectDir(QFileInfo(projectUrl).absoluteDir());

        foreach (const QString& category, categories)
        {
            const QStringList categorySplit =
                category.split('>', QString::SkipEmptyParts);

            if (categorySplit.size() > 0)
            {
                if (categorySplit.size() > 1)
                {
                    const QStringList documentsSplit =
                        categorySplit.at(1).split('|',
                        QString::SkipEmptyParts);

                    const QSet<QString> uniqueDocumentsSplit =
                        documentsSplit.toSet();

                    foreach (const QString& document, uniqueDocumentsSplit)
                    {
                        const QString absDocument =
                            projectDir.absoluteFilePath(document);

                        const QUrl url = QUrl::fromLocalFile(/*absDocument*/document);
                        if (_document->storage()->exists(url)) {
                            QSharedPointer<IPersistentDocument>
                            persistentDocument = Persistence::createDocument(
                                    url,
                                    _document->storage()->registeredName());

                            if (!persistentDocument.isNull())
                            {
                                QSharedPointer<IPersistenceAssistant>
                                    assistant =
                                        Persistence::getNativeAssistant(
                                            persistentDocument);

                                QSharedPointer<IDocument> childDocument =
                                    assistant->openFuture();

                                if (childDocument->isLinked() &&
                                    links.keys().contains(document))
                                {
                                    QSharedPointer<ILinkedDocument>
                                        linkedDoc =
                                            qSharedPointerDynamicCast
                                            <ILinkedDocument>(childDocument);

                                    Q_ASSERT(!linkedDoc.isNull()); // can't happen

                                    linksMapping[document] = linkedDoc;
                                }

                                projDocument->addDocument(categorySplit.at(0),
                                    childDocument);
                                childDocument->setContainer(projDocument);
                            }
                        }
                    }
                }
            }
        }

        foreach (const QString& linkedDoc, links.keys())
        {
            const QStringList& linkUrls = links[linkedDoc].split("|",
                QString::SkipEmptyParts);

            const QStringList categories = projDocument->categories();

            foreach (const QString& linkUrl, linkUrls)
            {
                const QString absLinkUrl =
                    projectDir.absoluteFilePath(linkUrl);
                const QUrl _linkUrl = QUrl::fromLocalFile(/*absLinkUrl*/linkUrl);
                if (_document->storage()->exists(_linkUrl)) {
                    foreach (const QString& category, categories)
                    {
                        const QList<Document> documents =
                            projDocument->documents(category);

                        bool shouldBreak = false;

                        foreach (const Document& document, documents)
                        {
                            const QSharedPointer<IPersistenceAssistant>
                                assistant = getNativeAssistant(document);

                            if (!assistant.isNull() &&
                                !assistant->getPersistentDocument().isNull()
                                && assistant->getPersistentDocument()->url()
                                == _linkUrl)
                            {
                                linksMapping[linkedDoc]->addLink(document,
                                    true);
                                shouldBreak = true;
                                break;
                            }
                        }

                        if (shouldBreak)
                        {
                            break;
                        }
                    }
                }
            }
        }

        ret = qSharedPointerDynamicCast<IDocument>(projDocument);
        ret->setFuture(false);

        if (ret != 0)
        {
            m_document = ret;
        }
    }
    else
    {
        ret = m_document;
    }

    return ret;
}

QSharedPointer<IDocument> FsProjectPersistenceAssistant::openFuture()
{
    Q_ASSERT(!m_persistentDocument.isNull());

    return open();
}

QSharedPointer<IPersistentDocument> FsProjectPersistenceAssistant::save()
{
    Q_ASSERT(!m_document.isNull());

    QSharedPointer<SwpProjectDocument> _document =
        qSharedPointerDynamicCast<SwpProjectDocument>(m_document);

    Q_ASSERT(_document != 0);

    core::DocumentCategories categories = _document->categories();

    QList<QString> serializedCategories;
    QHash<QString, QString> serializedLinks;

    const QString projectUrl =
        m_persistentDocument->url().toLocalFile();
    QDir projectDir(QFileInfo(projectUrl).absoluteDir());

    foreach (const QString& categoryName, categories)
    {
        const QList<QSharedPointer<IDocument> >& childDocuments =
            _document->documents(categoryName);

        QString category = QString("%1>").arg(categoryName);

        foreach (const QSharedPointer<IDocument>& childDocument,
            childDocuments)
        {
            QSharedPointer<IPersistenceAssistant> assistant =
                Persistence::getNativeAssistant(childDocument, forStorage());
            const QString documentUrl =
                assistant->getPersistentDocument()->url().toLocalFile();

            const QString relDocumentUrl =
                projectDir.relativeFilePath(documentUrl);

            category.append(QString("%1|").arg(relDocumentUrl));

            if (childDocument->isLinked())
            {
                const QSharedPointer<ILinkedDocument> linkedDoc =
                    qSharedPointerDynamicCast<ILinkedDocument>(childDocument);

                Q_ASSERT(!linkedDoc.isNull()); // can't happen

                const QList<QSharedPointer<IDocument> > links =
                    linkedDoc->links();

                if (links.size() > 0)
                {
                    QString linksForDoc;

                    for (int i = 0; i < links.size(); ++i)
                    {
                        QSharedPointer<IPersistenceAssistant> _assistant =
                            Persistence::getNativeAssistant(
                                links[i], forStorage());

                        const QString relLinkUrl =
                            projectDir.relativeFilePath(
                                _assistant->getPersistentDocument(
                                    )->url().toLocalFile());

                        linksForDoc += QString("%1|").arg(relLinkUrl);
                    }

                    serializedLinks[relDocumentUrl] = linksForDoc;
                }
            }
        }

        serializedCategories.append(category);
    }

    QByteArray data;

    QDataStream stream(&data, QIODevice::WriteOnly);
    stream.setVersion(QDataStream::Qt_4_7);

    stream << _document->name();
    stream << serializedCategories;
    stream << serializedLinks;

    m_persistentDocument->updateData(data);
    m_persistentDocument->storage()->write(m_persistentDocument);

    return m_persistentDocument;
}

void FsProjectPersistenceAssistant::closeAndKeepFuture()
{

}

bool FsProjectPersistenceAssistant::createPersistentDocument(const QUrl& url,
    const IStorage* storage)
{
    bool ret = false;

    QFileInfo info(url.toLocalFile());

    if (info.exists())
    {
        QSharedPointer<IPersistentDocument> document(
            new FsProjectPersistentDocument(info, storage));

        m_persistentDocument = document;

        ret = true;
    }

    return ret;
}

QSharedPointer<IDocument> FsProjectPersistenceAssistant::getDocument() const
{
    return m_document;
}

QSharedPointer<IPersistentDocument> FsProjectPersistenceAssistant::
    getPersistentDocument() const
{
    return m_persistentDocument;
}

IPersistenceAssistant* FsProjectPersistenceAssistant::copy() const
{
    FsProjectPersistenceAssistant* ret = new FsProjectPersistenceAssistant(
        m_mimeType);

    return ret;
}

void FsProjectPersistenceAssistant::setDocument(
    const QSharedPointer<IDocument>& document)
{
    if (m_document.isNull())
    {
        m_document = document;
    }
    else
    {
        Q_ASSERT(false); // document is already set
    }
}

void FsProjectPersistenceAssistant::setPersistentDocument(
    const QSharedPointer<IPersistentDocument>& document)
{
    if (m_persistentDocument.isNull())
    {
        m_persistentDocument = document;
    }
    else
    {
        Q_ASSERT(false); // document is already set
    }
}

void FsProjectPersistenceAssistant::clearFailedDocument(
    QSharedPointer<IContainerDocument>& document) const
{
    foreach (const QString& category, document->categories())
    {
        QList<QSharedPointer<IDocument> > documents =
            document->documents(category);

        foreach (const Document& childDocument, documents)
        {
            childDocument->setContainer(QSharedPointer<IContainerDocument>(0));
            document->removeDocument(category, childDocument);
        }
    }

    document.clear();
}

QSharedPointer<IPersistenceAssistant>
    FsProjectPersistenceAssistant::getNativeAssistant(
        const QSharedPointer<IDocument>& document) const
{
    QList<QSharedPointer<IPersistenceAssistant> > assistants =
        Persistence::getNativeAssistants(document);

    QSharedPointer<IPersistenceAssistant> assistant;

    foreach (const QSharedPointer<IPersistenceAssistant>& _assistant,
        assistants)
    {
        if (!_assistant->getPersistentDocument().isNull())
        {
            assistant = _assistant;
            break;
        }
        else
        {
            Persistence::closeAssistant(_assistant);
        }
    }

    return assistant;
}
